package org.hamcrest.generator;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaParameter;
import com.thoughtworks.qdox.model.Type;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
/**
* Wraps an existing sequence of FactoryMethods, and attempts to pull in
* parameter names and JavaDoc (which aren't available using reflection) using
* QDox.
*
* @see <a href="http://qdox.codehaus.org/">QDox</a>
* @author Joe Walnes
*/
public class QDoxFactoryReader implements Iterable<FactoryMethod> {
private final Iterable<FactoryMethod> wrapped;
private final JavaClass classSource;
private static final Pattern GENERIC_REGEX = Pattern.compile("<.*>");
private static final Pattern VARARGS_REGEX = Pattern.compile("...", Pattern.LITERAL);
public QDoxFactoryReader(Iterable<FactoryMethod> wrapped, QDox qdox, String className) {
this.wrapped = wrapped;
this.classSource = qdox.getClassByName(className);
}
public Iterator<FactoryMethod> iterator() {
final Iterator<FactoryMethod> iterator = wrapped.iterator();
return new Iterator<FactoryMethod>() {
public boolean hasNext() {
return iterator.hasNext();
}
public FactoryMethod next() {
return enhance(iterator.next());
}
public void remove() {
iterator.remove();
}
};
}
private FactoryMethod enhance(FactoryMethod factoryMethod) {
JavaMethod methodSource = findMethodInSource(factoryMethod);
if (methodSource != null) {
factoryMethod.setJavaDoc(createJavaDocComment(methodSource));
JavaParameter[] parametersFromSource
= methodSource.getParameters();
List<FactoryMethod.Parameter> parametersFromReflection
= factoryMethod.getParameters();
if (parametersFromReflection.size() == parametersFromSource.length) {
for (int i = 0; i < parametersFromSource.length; i++) {
parametersFromReflection.get(i).setName(
parametersFromSource[i].getName());
}
}
}
return factoryMethod;
}
/**
* Attempts to locate the source code for a specific method, by cross-referencing
* the signature returned by reflection with the list of methods parsed by QDox.
*/
private JavaMethod findMethodInSource(FactoryMethod factoryMethod) {
// Note, this doesn't always work - it struggles with some kinds of generics.
// This seems to cover most cases though.
List<FactoryMethod.Parameter> params = factoryMethod.getParameters();
Type[] types = new Type[params.size()];
for (int i = 0; i < types.length; i++) {
// QDox ignores varargs and generics, so we strip them out to help QDox.
String type = params.get(i).getType();
type = GENERIC_REGEX.matcher(type).replaceAll("");
type = VARARGS_REGEX.matcher(type).replaceAll("");
types[i] = new Type(type);
}
JavaMethod[] methods = classSource.getMethodsBySignature(factoryMethod.getName(), types, false);
return methods.length == 1 ? methods[0] : null;
}
/**
* Reconstructs the JavaDoc as a string for a particular method.
*/
private String createJavaDocComment(JavaMethod methodSource) {
String comment = methodSource.getComment();
DocletTag[] tags = methodSource.getTags();
if ((comment == null || comment.trim().length() == 0) && tags.length == 0) {
return null;
}
StringBuilder result = new StringBuilder();
result.append(comment);
result.append("\n\n");
for (DocletTag tag : tags) {
result.append('@').append(tag.getName())
.append(' ').append(tag.getValue())
.append('\n');
}
return result.toString();
}
}